using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace YouTubeDupe
{
    /// <summary>
    /// A class that manages a pool of SessionControllers.
    /// </summary>
    public class SessionPool
    {
        private static PoolState _state = PoolState.Stopped;
        private static List<SessionController> _sessions = new List<SessionController>();
        private static Random randGen = new Random(DateTime.Now.Millisecond);
        private static Thread _sessionManagerThread = new Thread(new ThreadStart(sessionManager));
        private static List<SessionController> _sessionsToRemove = new List<SessionController>();

        /// <summary>
        /// The current state of the SessionPool.
        /// </summary>
        public static PoolState State
        {
            get { return _state; }
        }

        /// <summary>
        /// The number of sessions in the pool.
        /// </summary>
        public static int Count
        {
            get { return _sessions.Count; }
        }

        /// <summary>
        /// Creates a new instance of a session controller and adds it to the session pool.
        /// </summary>
        /// <returns>A new session controller.</returns>
        public static SessionController CreateSession()
        {
            // Start the session manager thread if it's stopped.
            if (State == PoolState.Stopped)
            {
                _state = PoolState.Running;
                _sessionManagerThread.Start();
            }

            // Create a randum number to be used as the session id.
            int randNum = randGen.Next(0, int.MaxValue);

            // Search all sessions to see if the id exists.
            bool randNumExists = false;
            foreach (SessionController session in _sessions)
            {
                if (session.SessionID == randNum)
                {
                    randNumExists = true;
                    break;
                }
            }

            if (!randNumExists)
            {
                // If the id doesn't exist create a session and add it to the pool.
                SessionController newSession = new SessionController(randNum);
                lock (_sessions)
                {
                    _sessions.Add(newSession);
                }
                OnSessionAdded(new SessionChangedEventArgs(newSession));
                return newSession;
            }
            else
            {
                // If the id exists recursively call this operation until a unique id is found.
                return CreateSession();
            }
        }

        /// <summary>
        /// The thread that iterates the sessions in the pool and removes any that have completed.
        /// </summary>
        private static void sessionManager()
        {
            while (true)
            {
                _sessionsToRemove.Clear();

                foreach (SessionController session in _sessions)
                {
                    if (session.State == SessionController.CurrentState.Complete)
                    {
                        session.Stop();
                        _sessionsToRemove.Add(session);
                    }
                }


                foreach (SessionController session in _sessionsToRemove)
                {
                    lock (_sessions)
                    {
                        _sessions.Remove(session);
                        OnSessionRemoved(new SessionChangedEventArgs(session));
                    }
                }

                Thread.Sleep(2000);
            }
        }

        /// <summary>
        /// Stops all sessions in the pool.
        /// </summary>
        public static void StopAll()
        {
            lock (_sessions)
            {
                foreach (SessionController session in _sessions)
                {
                    session.Stop();
                }

                _sessions.Clear();
            }

            _sessionManagerThread.Abort();
        }

        /// <summary>
        /// Triggered when a session is added to the pool.
        /// </summary>
        public static event SessionChangedEventHanlder SessionAdded;

        /// <summary>
        /// Triggered when a session is removed from the pool.
        /// </summary>
        public static event SessionChangedEventHanlder SessionRemoved;

        /// <summary>
        /// Handles the SessionAdded event.
        /// </summary>
        private static void OnSessionAdded(SessionChangedEventArgs e)
        {
            if (SessionAdded != null)
            {
                SessionAdded(null, e);
            }
        }

        /// <summary>
        /// Handles the SessionRemoved event.
        /// </summary>
        private static void OnSessionRemoved(SessionChangedEventArgs e)
        {
            if (SessionRemoved != null)
            {
                SessionRemoved(null, e);
            }
        }

        /// <summary>
        /// The possible states of the SessionPool.
        /// </summary>
        public enum PoolState
        {
            Stopped, Running
        }

        /// <summary>
        /// Returns an enumerator that iterates through the Pool.
        /// </summary>
        public static List<SessionController>.Enumerator GetEnumerator()
        {
            return _sessions.GetEnumerator();
        }
    }

    /// <summary>
    /// The delegate used to handle the SessionAdded and SessionRemoved events.
    /// </summary>
    public delegate void SessionChangedEventHanlder(object sender, SessionChangedEventArgs e);

    /// <summary>
    /// Provides arguments for the SessionAdded and SessionRemoved events.
    /// </summary>
    public class SessionChangedEventArgs
    {
        SessionController _session;

        /// <summary>
        /// Creates a new instance of the SessionChangedEventArgs class.
        /// </summary>
        /// <param name="session"></param>
        public SessionChangedEventArgs(SessionController session)
        {
            _session = session;
        }

        /// <summary>
        /// The corresponding session.
        /// </summary>
        public SessionController Session
        {
            get { return _session; }
        }
    }
}
